/**
 * 
 */
package com.zhisheng.core.config;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

import com.zhisheng.core.utils.ReflectionUtils;
import com.zhisheng.core.utils.TypeConverterUtils;



/**
 * @author Downpour
 */
public class AnnotationPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer implements BeanPostProcessor {

	 private static final Log logger = LogFactory.getLog(AnnotationPropertyPlaceholderConfigurer.class);  
	       
	 private Properties properties;

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
	 */
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
	 */
	public Object postProcessBeforeInitialization(Object bean, String beanName)	throws BeansException {

	    // inject for field
		Field[] fields = bean.getClass().getDeclaredFields();  
		
        for (Field field : fields) {
        	ReflectionUtils.makeAccessible(field);
            if (field.isAnnotationPresent(Inject.class)) {
				Inject inject = field.getAnnotation(Inject.class);
				
				if(StringUtils.isNotEmpty(inject.group())) {
					Map<String, Map<String, String>>  prefixGroupedProperties = this.getPrefixGroupedProperties(); 
					if(prefixGroupedProperties.containsKey(inject.group())) {
						try {
							ReflectionUtils.setField(field, bean, prefixGroupedProperties.get(inject.group()));
						} catch (Exception e) {
							logger.error(e);
						}
					}
				} else {
					try {
					    String value = properties.getProperty(inject.value());
					    if(StringUtils.isNotBlank(value))
					    	ReflectionUtils.setField(field, bean, value);
					} catch (Exception e) {
						logger.error(e);
					}
				}
			}  
        }

        // inject for method
        Method[] methods = bean.getClass().getDeclaredMethods();
        for(Method method : methods) {
            if(method.isAnnotationPresent(Inject.class) && method.getParameterTypes().length == 1) {
                Inject inject = method.getAnnotation(Inject.class);
                try {
                    String value = properties.getProperty(inject.value());
                    if(StringUtils.isNotBlank(value)) {
	                    if(method.getParameterTypes()[0].isPrimitive())
	                    	method.invoke(bean, TypeConverterUtils.parse(value, method.getParameterTypes()[0]));
	                    else if(method.getParameterTypes()[0].isAssignableFrom(String.class))
	                    	method.invoke(bean, value);
                    } else {
                    	logger.warn("inject " + bean.getClass().getSimpleName() + "'s method - " + method.getName() +
                    			", the value is null");
                    }
                } catch (Exception e) {
                	logger.error(e.getMessage());
                }
            }
        }
        
        return bean;  
	}

	@Override
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			Properties props) throws BeansException {
		super.processProperties(beanFactoryToProcess, props);
		this.properties = props;
	}
	
	/**
	 * Get prefix grouped properties
	 * 
	 * @return
	 */
	public Map<String, Map<String, String>> getPrefixGroupedProperties() {
		Map<String, Map<String, String>> prefixGroupedProperties = new HashMap<String, Map<String, String>>();
		for(Object propertyKey : this.properties.keySet()) {
			String key = (String) propertyKey;
			int index = key.indexOf(".");
			String prefix = key.substring(0, index);
			
			Map<String, String> propertyMap = prefixGroupedProperties.get(prefix);
			if(propertyMap == null) {
				propertyMap = new HashMap<String, String>();
			} 

			propertyMap.put(key.substring(index + 1), this.properties.getProperty(key));
			prefixGroupedProperties.put(prefix, propertyMap);
		}
		return prefixGroupedProperties;
	}
}
